/**
 * *****************************************
 * Project: cone
 * File: SpringResourceHandlerContext.java
 * Creation Date: 3:04:15 PM
 * Creator: Ahmed Fawzy
 * *****************************************
 * Copyright © 2012 Devariance.
 */
package com.devariance.cone.handler.impl;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.devariance.cone.handler.ResourceHandler;
import com.devariance.cone.handler.ResourceHandlerClassLoader;
import com.devariance.cone.handler.ResourceHandlerContext;
import com.devariance.cone.handler.annotation.Handler;
import com.devariance.cone.handler.exception.InvalidResourceHandlerException;
import com.devariance.cone.handler.exception.ResourceHandlerException;
import com.devariance.cone.logging.HasLogger;
import com.devariance.cone.resource.Resource;

/**
 * @author Ahmed Fawzy
 * 
 */
public class SpringResourceHandlerContext implements ResourceHandlerContext,
		ApplicationContextAware, HasLogger {

	private ResourceHandlerClassLoader resourceHandlerClassLoader;
	private ApplicationContext applicationContext;
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	private Map<Handler, ResourceHandler<? extends Resource>> resourceHandlers = null;

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.devariance.cone.handler.ResourceHandlerContext#configure()
	 */
	@Override
	public void configure() throws ResourceHandlerException {
		if (this.resourceHandlerClassLoader == null) {
			throw new ResourceHandlerException(
					"null ResourceHandlerClassLoader found"); //$NON-NLS-1$
		}

		this.resourceHandlers = this.getResourceHandlers();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.devariance.cone.logging.HasLogger#getLogger()
	 */
	@Override
	public Logger getLogger() {
		return this.logger;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.devariance.cone.handler.ResourceHandlerContext#getResourceHandler
	 * (java.lang.String)
	 */
	@Override
	public ResourceHandler<? extends Resource> getResourceHandler(
			final String resourceHandlerName)
			throws InvalidResourceHandlerException {
		ResourceHandler<? extends Resource> resourceHandler = null;

		resourceHandler = this.resourceHandlers.get(resourceHandlerName);

		if (resourceHandler == null) {
			throw new InvalidResourceHandlerException(new StringBuilder()
					.append("no resource handler could be found with name '") //$NON-NLS-1$
					.append(resourceHandlerName).append("'") //$NON-NLS-1$
					.toString());
		}

		return resourceHandler;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.devariance.cone.handler.ResourceHandlerContext#
	 * getResourceHandlerClassLoader()
	 */
	@Override
	public ResourceHandlerClassLoader getResourceHandlerClassLoader() {
		return this.resourceHandlerClassLoader;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.devariance.cone.handler.ResourceHandlerContext#getResourceHandlers()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Map<Handler, ResourceHandler<? extends Resource>> getResourceHandlers() {

		if (this.resourceHandlers != null) {
			return this.resourceHandlers;
		}

		@SuppressWarnings("rawtypes")
		final Map<String, ResourceHandler> map = this.applicationContext
				.getBeansOfType(ResourceHandler.class, false, true);

		final Map<Handler, ResourceHandler<? extends Resource>> resourceHandlersMap = new HashMap<Handler, ResourceHandler<? extends Resource>>();

		for (@SuppressWarnings("rawtypes")
		final Map.Entry<String, ResourceHandler> resourceHandlerEntry : map
				.entrySet()) {
			if (resourceHandlerEntry.getValue() == null) {
				continue;
			}

			final Handler handlerAnnotation = resourceHandlerEntry.getValue()
					.getClass().getAnnotation(Handler.class);

			if (handlerAnnotation == null) {
				continue;
			}

			if (resourceHandlersMap.containsKey(handlerAnnotation.name())) {
				if (this.logger.isWarnEnabled()) {
					this.logger.warn(new StringBuilder(
							"redundancy in Handler mapping in bean '") //$NON-NLS-1$
							.append(resourceHandlerEntry.getKey())
							.append("' which will be ignored").toString()); //$NON-NLS-1$
				}

				continue;
			}

			resourceHandlersMap.put(handlerAnnotation,
					resourceHandlerEntry.getValue());
		}

		return resourceHandlersMap;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.devariance.cone.handler.ResourceHandlerContext#reconfigure()
	 */
	@Override
	public void reconfigure() throws ResourceHandlerException {
		this.release();
		this.configure();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.devariance.cone.handler.ResourceHandlerContext#release()
	 */
	@Override
	public void release() throws ResourceHandlerException {
		this.resourceHandlers = null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.context.ApplicationContextAware#setApplicationContext
	 * (org.springframework.context.ApplicationContext)
	 */
	@Override
	public void setApplicationContext(
			final ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.devariance.cone.logging.HasLogger#setLogger(org.slf4j.Logger)
	 */
	@Override
	public void setLogger(final Logger logger) {
		this.logger = logger;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.devariance.cone.handler.ResourceHandlerContext#
	 * setResourceHandlerClassLoader
	 * (com.devariance.cone.handler.ResourceHandlerClassLoader)
	 */
	@Override
	public void setResourceHandlerClassLoader(
			final ResourceHandlerClassLoader resourceHandlerClassLoader) {
		this.resourceHandlerClassLoader = resourceHandlerClassLoader;
	}
}
